package phi.engine 
{
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2DebugDraw;
	import Box2D.Dynamics.b2World;
	
	import flash.display.Scene;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.system.ApplicationDomain;
	import flash.utils.getTimer;
	
	import phi.engine.actors.IActor;
	import phi.engine.core.GameTime;
	import phi.engine.core.IGame;
	import phi.engine.input.KeyboardState;
	import phi.engine.layers.ILayer;
	import phi.engine.scene.SceneManager;
	import phi.engine.utils.PhiMathUtil;
	
	/**
	 * ...
	 * @author Alexandru Ghiura
	 */
	public class PhiEngine 
	{
		// Static consts
		static public const FRAME_RATE :Number = 60;
		static public const STEPS :Number = 10;
				
		// Singleton consts
		static private const SINGLETON_MSG :String = "Only one Registry instance should be instantiated";
		static private var instance :PhiEngine;
		
		// Pulibc var
		public var pause :Boolean = false;
		
		// Protected vars
		protected var debugSprite :Sprite;
		protected var gametTime :GameTime = new GameTime();
		protected var keys :Array = [];
		protected var stage :Stage;
		
		// Private vars
		private var _debugDraw :Boolean = false;
		private var _game :IGame = null;

		/**
		 * Use getInstance() instead of constructor.
		 */
		public function PhiEngine()
		{
			if ( instance != null )
			{
				throw new Error( SINGLETON_MSG );	
			}
		}

		/**
		 * Singleton Server Factory Method
		 */
		public static function getInstance():PhiEngine
		{
			if ( instance == null ) instance = new PhiEngine();
				return instance;
		}

		// --------------------------------------------------------------------
		// Public Get/Set functions
		// --------------------------------------------------------------------
		
		public function get debugDraw():Boolean
		{
			return _debugDraw;
		}
		
		public function set debugDraw(value:Boolean):void
		{
			_debugDraw = value;
			
			if( debugDraw )
				game.world.SetDebugDraw( createDebugDraw() );
			else
				game.world.SetDebugDraw( null );
		
		}
		
		public function get screenWidth():Number
		{
			return stage.stageWidth
		}
		
		public function get screenHeight():Number
		{
			return stage.stageHeight;
		}
		
		public function get game():IGame
		{
			return _game;
		}
		
		public function keyboardState():KeyboardState
		{
			return new KeyboardState( keys );
		}
		
		// --------------------------------------------------------------------
		// Public functions
		// --------------------------------------------------------------------
		
		/**
		 * Setup engine
		 * @param	display
		 */
		public function setup( game :IGame, stage :Stage ):void
		{
			// Init debug draw
			debugSprite = new Sprite();
			
			// Init stage
			this.stage = stage;
			this.stage.addEventListener( KeyboardEvent.KEY_DOWN, keyDownHandler );
			this.stage.addEventListener( KeyboardEvent.KEY_UP, keyUpHandler );
			this.stage.addChild( debugSprite );
			
			// Init game
			this._game = game;
			this._game.init();
		}
		
		public function run():void
		{
			gametTime = new GameTime();
			game.window.addEventListener( Event.ENTER_FRAME, update );
		}
		
		// --------------------------------------------------------------------
		// Protected functions
		// --------------------------------------------------------------------
		
		/**
		 * Update all actors
		 * @param	event
		 */
		protected function update( event:Event ):void
		{
			// Update gameTime
			gametTime.update();
		
			// Update game
			if( !pause )
				game.update( gametTime );
			
			game.handleInput();
			
			// Draw debug data
			if( debugDraw )
				game.world.DrawDebugData();
		}
		
		protected function createDebugDraw():b2DebugDraw
		{
			var dbgDraw :b2DebugDraw = new b2DebugDraw();
			
			dbgDraw.SetSprite( debugSprite );
			dbgDraw.SetDrawScale( PhiMathUtil.METERS_RATION );
			dbgDraw.SetAlpha( 1 );
			dbgDraw.SetFillAlpha( 0.5 );
			dbgDraw.SetLineThickness( 1 );
			dbgDraw.SetFlags( b2DebugDraw.E_SHAPEBIT | b2DebugDraw.E_JOINTBIT );
			
			return dbgDraw;
		}
		
		
		private function keyDownHandler( event:KeyboardEvent ):void
		{
			keys[ event.keyCode ] = true;
		}
		
		private function keyUpHandler( event:KeyboardEvent ):void
		{
			keys[ event.keyCode ] = false;
		}
	}

}